home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / jade / src / movement.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  25KB  |  932 lines

  1. /* movement.c -- Positioning the cursor
  2.    Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
  3.  
  4.    This file is part of Jade.
  5.  
  6.    Jade is free software; you can redistribute it and/or modify it
  7.    under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2, or (at your option)
  9.    any later version.
  10.  
  11.    Jade is distributed in the hope that it will be useful, but
  12.    WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with Jade; see the file COPYING.  If not, write to
  18.    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "jade.h"
  21. #include "jade_protos.h"
  22.  
  23. #include <ctype.h>
  24.  
  25. static long move_down_screens(long);
  26. static long move_up_screens(long);
  27. static bool prev_char(long, POS *, TX *);
  28. static bool next_char(long, POS *, TX *);
  29. static int find_matching_bracket(POS *, TX *, u_char esc);
  30. _PR void movement_init(void);
  31.  
  32. _PR VALUE cmd_screen_top_line(void);
  33. DEFUN("screen-top-line", cmd_screen_top_line, subr_screen_top_line, (void), V_Subr0, DOC_screen_top_line) /*
  34. ::doc:screen_top_line::
  35. screen-top-line
  36.  
  37. Returns the line number of the first line being shown in the current window.
  38. ::end:: */
  39. {
  40.     return(make_number(curr_vw->vw_StartLine));
  41. }
  42.  
  43. _PR VALUE cmd_screen_bottom_line(void);
  44. DEFUN("screen-bottom-line", cmd_screen_bottom_line, subr_screen_bottom_line, (void), V_Subr0, DOC_screen_bottom_line) /*
  45. ::doc:screen_bottom_line::
  46. screen-bottom-line
  47.  
  48. Returns the line number of the last line being shown in the current window.
  49. ::end:: */
  50. {
  51.     return(make_number(curr_vw->vw_StartLine + curr_vw->vw_MaxY - 1));
  52. }
  53.  
  54. _PR VALUE cmd_screen_first_column(void);
  55. DEFUN("screen-first-column", cmd_screen_first_column, subr_screen_first_column, (void), V_Subr0, DOC_screen_first_column) /*
  56. ::doc:screen_first_column::
  57. screen-first-column
  58.  
  59. Returns the line number of the first column being shown in the current window.
  60. ::end:: */
  61. {
  62.     return(make_number(curr_vw->vw_StartCol));
  63. }
  64.  
  65. _PR VALUE cmd_screen_last_column(void);
  66. DEFUN("screen-last-column", cmd_screen_last_column, subr_screen_last_column, (void), V_Subr0, DOC_screen_last_column) /*
  67. ::doc:screen_last_column::
  68. screen-last-column
  69.  
  70. Returns the line number of the last column being shown in the current window.
  71. ::end:: */
  72. {
  73.     return(make_number(curr_vw->vw_StartCol + curr_vw->vw_MaxX - 1));
  74. }
  75.  
  76. _PR VALUE cmd_goto_char(VALUE pos);
  77. DEFUN("goto-char", cmd_goto_char, subr_goto_char, (VALUE pos), V_Subr1, DOC_goto_char) /*
  78. ::doc:goto_char::
  79. goto-char POS
  80.  
  81. Set the cursor position in the current window to the character position POS.
  82. ::end:: */
  83. {
  84.     VW *vw = curr_vw;
  85.     DECLARE1(pos, POSP);
  86.     if(check_line(vw->vw_Tx, &VPOS(pos)))
  87.     {
  88.     vw->vw_CursorPos = VPOS(pos);
  89.     return(pos);
  90.     }
  91.     return(sym_nil);
  92. }
  93.  
  94. _PR VALUE cmd_goto_glyph(VALUE pos);
  95. DEFUN("goto-glyph", cmd_goto_glyph, subr_goto_glyph, (VALUE pos), V_Subr1, DOC_goto_glyph) /*
  96. ::doc:goto_glyph::
  97. goto-glyph POS
  98.  
  99. Set the cursor position in the current window to the glyph position POS.
  100. ::end:: */
  101. {
  102.     VW *vw = curr_vw;
  103.     DECLARE1(pos, POSP);
  104.     if(check_line(vw->vw_Tx, &VPOS(pos)))
  105.     {
  106.     vw->vw_CursorPos.pos_Col = char_col(vw->vw_Tx, VPOS(pos).pos_Col,
  107.                         VPOS(pos).pos_Line);
  108.     vw->vw_CursorPos.pos_Line = VPOS(pos).pos_Line;
  109.     return(pos);
  110.     }
  111.     return(sym_nil);
  112. }
  113.  
  114. _PR VALUE cmd_centre_display(VALUE vw);
  115. DEFUN_INT("centre-display", cmd_centre_display, subr_centre_display, (VALUE vw), V_Subr1, DOC_centre_display, "") /*
  116. ::doc:centre_display::
  117. centre-display [WINDOW]
  118.  
  119. Arrange it so that the line that the cursor is on is displayed in the
  120. middle of the window (if possible).
  121. ::end:: */
  122. {
  123.     long start_line;
  124.     if(!WINDOWP(vw))
  125.     vw = VAL(curr_vw);
  126.     start_line = VWIN(vw)->vw_CursorPos.pos_Line - (VWIN(vw)->vw_MaxY / 2);
  127.     if(start_line < 0)
  128.     start_line = 0;
  129.     if(start_line >= VWIN(vw)->vw_Tx->tx_NumLines)
  130.     start_line = VWIN(vw)->vw_Tx->tx_NumLines - 1;
  131.     VWIN(vw)->vw_StartLine = start_line;
  132.     return(vw);
  133. }
  134.  
  135. _PR VALUE cmd_next_screen(VALUE number);
  136. DEFUN_INT("next-screen", cmd_next_screen, subr_next_screen, (VALUE number), V_Subr1, DOC_next_screen, "p") /*
  137. ::doc:next_screen::
  138. next-screen [NUMBER]
  139.  
  140. Move NUMBER (default: 1) screens forwards in the current window.
  141. ::end:: */
  142. {
  143.     if(move_down_screens(NUMBERP(number) ? VNUM(number) : 1))
  144.     return(sym_t);
  145.     return(sym_nil);
  146. }
  147.  
  148. _PR VALUE cmd_prev_screen(VALUE number);
  149. DEFUN_INT("prev-screen", cmd_prev_screen, subr_prev_screen, (VALUE number), V_Subr1, DOC_prev_screen, "p") /*
  150. ::doc:prev_screen::
  151. prev-screen [NUMBER]
  152.  
  153. Move NUMBER (default: 1) screens backwards in the current window.
  154. ::end:: */
  155. {
  156.     if(move_up_screens(NUMBERP(number) ? VNUM(number) : 1))
  157.     return(sym_t);
  158.     return(sym_nil);
  159. }
  160.  
  161. _PR VALUE cmd_buffer_end(VALUE tx);
  162. DEFUN("buffer-end", cmd_buffer_end, subr_buffer_end, (VALUE tx), V_Subr1, DOC_buffer_end) /*
  163. ::doc:buffer_end::
  164. buffer-end [BUFFER]
  165.  
  166. Return the position of the last character in BUFFER.
  167. ::end:: */
  168. {
  169.     long x, y;
  170.     if(!BUFFERP(tx))
  171.     tx = VAL(curr_vw->vw_Tx);
  172.     y = VTX(tx)->tx_NumLines - 1;
  173.     x = VTX(tx)->tx_Lines[y].ln_Strlen - 1;
  174.     return(make_lpos2(x, y));
  175. }
  176.  
  177. _PR VALUE cmd_goto_buffer_end(void);
  178. DEFUN_INT("goto-buffer-end", cmd_goto_buffer_end, subr_goto_buffer_end, (void), V_Subr0, DOC_goto_buffer_end, "") /*
  179. ::doc:goto_buffer_end::
  180. goto-buffer-end
  181.  
  182. Move to the last character in the current window.
  183. ::end:: */
  184. {
  185.     VW *vw = curr_vw;
  186.     vw->vw_CursorPos.pos_Line = vw->vw_Tx->tx_NumLines - 1;
  187.     vw->vw_CursorPos.pos_Col = vw->vw_Tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen - 1;
  188.     return(sym_t);
  189. }
  190.  
  191. _PR VALUE cmd_buffer_start(void);
  192. DEFUN("buffer-start", cmd_buffer_start, subr_buffer_start, (void), V_Subr0, DOC_buffer_start) /*
  193. ::doc:buffer_start::
  194. buffer-start
  195.  
  196. Return the position of the start of the buffer.
  197. ::end:: */
  198. {
  199.     return(make_lpos2(0, 0));
  200. }
  201.  
  202. _PR VALUE cmd_goto_buffer_start(void);
  203. DEFUN_INT("goto-buffer-start", cmd_goto_buffer_start, subr_goto_buffer_start, (void), V_Subr0, DOC_goto_buffer_start, "") /*
  204. ::doc:goto_buffer_start::
  205. goto-buffer-start
  206.  
  207. Move to the first character in the buffer displayed in the current window.
  208. ::end:: */
  209. {
  210.     curr_vw->vw_CursorPos.pos_Col = 0;
  211.     curr_vw->vw_CursorPos.pos_Line = 0;
  212.     return(sym_t);
  213. }
  214.  
  215. _PR VALUE cmd_line_end(VALUE pos, VALUE tx);
  216. DEFUN("line-end", cmd_line_end, subr_line_end, (VALUE pos, VALUE tx), V_Subr2, DOC_line_end) /*
  217. ::doc:line_end::
  218. line-end [POS] [BUFFER]
  219.  
  220. Return the position of the last character in the line pointed to by POS (or
  221. the cursor).
  222. ::end:: */
  223. {
  224.     POS res;
  225.     if(!BUFFERP(tx))
  226.     tx = VAL(curr_vw->vw_Tx);
  227.     if(POSP(pos))
  228.     res.pos_Line = VPOS(pos).pos_Line;
  229.     else
  230.     res.pos_Line = get_tx_cursor(VTX(tx))->pos_Line;
  231.     if(res.pos_Line < VTX(tx)->tx_NumLines)
  232.     {
  233.     res.pos_Col = VTX(tx)->tx_Lines[res.pos_Line].ln_Strlen - 1;
  234.     return(make_lpos(&res));
  235.     }
  236.     return(sym_nil);
  237. }
  238.  
  239. _PR VALUE cmd_goto_line_end(void);
  240. DEFUN_INT("goto-line-end", cmd_goto_line_end, subr_goto_line_end, (void), V_Subr0, DOC_goto_line_end, "") /*
  241. ::doc:goto_line_end::
  242. goto-line-end
  243.  
  244. Move to the last character in the line.
  245. ::end:: */
  246. {
  247.     VW *vw = curr_vw;
  248.     vw->vw_CursorPos.pos_Col = vw->vw_Tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen - 1;
  249.     return(sym_t);
  250. }
  251.  
  252. _PR VALUE cmd_line_start(VALUE pos);
  253. DEFUN("line-start", cmd_line_start, subr_line_start, (VALUE pos), V_Subr1, DOC_line_start) /*
  254. ::doc:line_start::
  255. line-start [POS]
  256.  
  257. Return the position of the first character in the line pointed to by POS
  258. (or the cursor).
  259. ::end:: */
  260. {
  261.     POS res;
  262.     if(POSP(pos))
  263.     res.pos_Line = VPOS(pos).pos_Line;
  264.     else
  265.     res.pos_Line = curr_vw->vw_CursorPos.pos_Line;
  266.     res.pos_Col = 0;
  267.     return(make_lpos(&res));
  268. }
  269.  
  270. _PR VALUE cmd_goto_line_start(void);
  271. DEFUN_INT("goto-line-start", cmd_goto_line_start, subr_goto_line_start, (void), V_Subr0, DOC_goto_line_start, "") /*
  272. ::doc:goto_line_start::
  273. goto-line-start
  274.  
  275. Move to the start of the current line.
  276. ::end:: */
  277. {
  278.     VW *vw = curr_vw;
  279.     vw->vw_CursorPos.pos_Col = 0;
  280.     return(sym_t);
  281. }
  282.  
  283. _PR VALUE cmd_next_line(VALUE lines, VALUE pos);
  284. DEFUN("next-line", cmd_next_line, subr_next_line, (VALUE lines, VALUE pos), V_Subr2, DOC_next_line) /*
  285. ::doc:next_line::
  286. next-line [NUMBER] [POS]
  287.  
  288. Return the position of the NUMBERth (def: 1) line down from that pointed to
  289. by POS (or the cursor). POS is altered.
  290. ::end:: */
  291. {
  292.     if(!POSP(pos))
  293.     pos = make_lpos(&curr_vw->vw_CursorPos);
  294.     VPOS(pos).pos_Line += NUMBERP(lines) ? VNUM(lines) : 1;
  295.     if(VPOS(pos).pos_Line > 0)
  296.     return(pos);
  297.     return(sym_nil);
  298. }
  299.  
  300. _PR VALUE cmd_goto_next_line(VALUE lines);
  301. DEFUN_INT("goto-next-line", cmd_goto_next_line, subr_goto_next_line, (VALUE lines), V_Subr1, DOC_goto_next_line, "p") /*
  302. ::doc:goto_next_line::
  303. goto-next-line [NUMBER]
  304.  
  305. Move NUMBER lines (def: 1) downwards. Adjusts the cursor's column position
  306. so that it is drawn as near to where it was previously as possible.
  307. ::end:: */
  308. {
  309.     VW *vw = curr_vw;
  310.     VALUE res = sym_nil;
  311.     vw->vw_CursorPos.pos_Line += NUMBERP(lines) ? VNUM(lines) : 1;
  312.     if(vw->vw_CursorPos.pos_Line >= vw->vw_Tx->tx_NumLines)
  313.     vw->vw_CursorPos.pos_Line = vw->vw_Tx->tx_NumLines - 1;
  314.     else if(vw->vw_CursorPos.pos_Line < 0)
  315.     vw->vw_CursorPos.pos_Line = 0;
  316.     else
  317.     res = sym_t;
  318.     adjust_cursor_to_glyph(vw);
  319.     return(res);
  320. }
  321.  
  322. _PR VALUE cmd_prev_line(VALUE lines, VALUE pos);
  323. DEFUN("prev-line", cmd_prev_line, subr_prev_line, (VALUE lines, VALUE pos), V_Subr2, DOC_prev_line) /*
  324. ::doc:prev_line::
  325. prev-line [NUMBER] [POS]
  326.  
  327. Return the position of the NUMBERth (def: 1) line up from that pointed to
  328. by POS (or the cursor). POS is altered.
  329. ::end:: */
  330. {
  331.     if(!POSP(pos))
  332.     pos = make_lpos(&curr_vw->vw_CursorPos);
  333.     VPOS(pos).pos_Line -= NUMBERP(lines) ? VNUM(lines) : 1;
  334.     if(VPOS(pos).pos_Line >= 0)
  335.     return(pos);
  336.     return(sym_nil);
  337. }
  338.  
  339. _PR VALUE cmd_goto_prev_line(VALUE lines);
  340. DEFUN_INT("goto-prev-line", cmd_goto_prev_line, subr_goto_prev_line, (VALUE lines), V_Subr1, DOC_goto_prev_line, "p") /*
  341. ::doc:goto_prev_line::
  342. goto-next-line [NUMBER]
  343.  
  344. Move NUMBER lines (def: 1) upwards. Adjusts the cursor's column position
  345. so that it is drawn as near to where it was previously as possible.
  346. ::end:: */
  347. {
  348.     VW *vw = curr_vw;
  349.     VALUE res = sym_nil;
  350.     vw->vw_CursorPos.pos_Line -= NUMBERP(lines) ? VNUM(lines) : 1;
  351.     if(vw->vw_CursorPos.pos_Line >= vw->vw_Tx->tx_NumLines)
  352.     vw->vw_CursorPos.pos_Line = vw->vw_Tx->tx_NumLines - 1;
  353.     else if(vw->vw_CursorPos.pos_Line < 0)
  354.     vw->vw_CursorPos.pos_Line = 0;
  355.     else
  356.     res = sym_t;
  357.     adjust_cursor_to_glyph(vw);
  358.     return(res);
  359. }
  360.  
  361. _PR VALUE cmd_left_char(VALUE chars, VALUE pos);
  362. DEFUN("left-char", cmd_left_char, subr_left_char, (VALUE chars, VALUE pos), V_Subr2, DOC_left_char) /*
  363. ::doc:left_char::
  364. left-char [NUMBER] [POS]
  365.  
  366. Return the position of the NUMBERth character (def: 1) to the left of the
  367. one pointed to by POS (or the cursor). If that position is before the
  368. beginning of the line, returns nil. POS is altered.
  369. ::end:: */
  370. {
  371.     if(!POSP(pos))
  372.     pos = make_lpos(&curr_vw->vw_CursorPos);
  373.     VPOS(pos).pos_Col -= NUMBERP(chars) ? VNUM(chars) : 1;
  374.     if(VPOS(pos).pos_Col >= 0)
  375.     return(pos);
  376.     return(sym_nil);
  377. }
  378.  
  379. _PR VALUE cmd_goto_left_char(VALUE chars);
  380. DEFUN_INT("goto-left-char", cmd_goto_left_char, subr_goto_left_char, (VALUE chars), V_Subr1, DOC_goto_left_char, "p") /*
  381. ::doc:goto_left_char::
  382. goto-left-char [NUMBER]
  383.  
  384. Move NUMBER chars (def: 1) to the left.
  385. ::end:: */
  386. {
  387.     VW *vw = curr_vw;
  388.     vw->vw_CursorPos.pos_Col -= NUMBERP(chars) ? VNUM(chars) : 1;
  389.     if(vw->vw_CursorPos.pos_Col > 0)
  390.     return(sym_t);
  391.     vw->vw_CursorPos.pos_Col = 0;
  392.     return(sym_nil);
  393. }
  394.  
  395. _PR VALUE cmd_right_char(VALUE chars, VALUE pos);
  396. DEFUN("right-char", cmd_right_char, subr_right_char, (VALUE chars, VALUE pos), V_Subr2, DOC_right_char) /*
  397. ::doc:right_char::
  398. right-char [NUMBER] [POS]
  399.  
  400. Return the position of the NUMBERth character (def: 1) to the right of the
  401. one pointed to by POS (or the cursor). Doesn't pay any attention to newlines.
  402. POS is altered.
  403. ::end:: */
  404. {
  405.     if(!POSP(pos))
  406.     pos = make_lpos(&curr_vw->vw_CursorPos);
  407.     VPOS(pos).pos_Col += NUMBERP(chars) ? VNUM(chars) : 1;
  408.     if(VPOS(pos).pos_Col >= 0)
  409.     return(pos);
  410.     return(sym_nil);
  411. }
  412.  
  413. _PR VALUE cmd_goto_right_char(VALUE chars);
  414. DEFUN_INT("goto-right-char", cmd_goto_right_char, subr_goto_right_char, (VALUE chars), V_Subr1, DOC_goto_right_char, "p") /*
  415. ::doc:goto_right_char::
  416. goto-right-char [NUMBER]
  417.  
  418. Move NUMBER chars (def: 1) to the right
  419. ::end:: */
  420. {
  421.     VW *vw = curr_vw;
  422.     vw->vw_CursorPos.pos_Col += NUMBERP(chars) ? VNUM(chars) : 1;
  423.     if(vw->vw_CursorPos.pos_Col > 0)
  424.     return(sym_t);
  425.     vw->vw_CursorPos.pos_Col = 0;
  426.     return(sym_nil);
  427. }
  428.  
  429. _PR VALUE cmd_prev_tab(VALUE num, VALUE pos, VALUE size);
  430. DEFUN("prev-tab", cmd_prev_tab, subr_prev_tab, (VALUE num, VALUE pos, VALUE size), V_Subr3, DOC_prev_tab) /*
  431. ::doc:prev_tab::
  432. prev-tab [NUMBER] [POS] [TAB-SIZE]
  433.  
  434. Return the position of the NUMBERth (def: 1) tab stop to the left of POS (or
  435. the cursor). Returns nil if that position is past the beginning of the line.
  436. Note that this doesn't return the actual *character* position of the tab
  437. stop, but the number of glyphs which have to be displayed. POS is altered.
  438. ::end:: */
  439. {
  440.     int tabs = 1;
  441.     VW *vw = curr_vw;
  442.     int tabsize = NUMBERP(size) ? VNUM(size) : vw->vw_Tx->tx_TabSize;
  443.     if(!POSP(pos))
  444.     {
  445.     pos = make_lpos(&curr_vw->vw_CursorPos);
  446.     calc_cursor_offset(vw);
  447.     VPOS(pos).pos_Col = vw->vw_LastCursorOffset;
  448.     }
  449.     if(NUMBERP(num))
  450.     tabs = VNUM(num);
  451.     if(tabs > 0)
  452.     {
  453.     while(tabs--)
  454.         VPOS(pos).pos_Col = (((VPOS(pos).pos_Col - 1) / tabsize)) * tabsize;
  455.     }
  456.     else if(tabs < 0)
  457.     {
  458.     while(tabs++)
  459.         VPOS(pos).pos_Col = ((VPOS(pos).pos_Col / tabsize) + 1) * tabsize;
  460.     }
  461.     if(VPOS(pos).pos_Col >= 0)
  462.     return(pos);
  463.     VPOS(pos).pos_Col = 0;
  464.     return(sym_nil);
  465. }
  466.  
  467. _PR VALUE cmd_goto_prev_tab(VALUE num, VALUE size);
  468. DEFUN_INT("goto-prev-tab", cmd_goto_prev_tab, subr_goto_prev_tab, (VALUE num, VALUE size), V_Subr2, DOC_goto_prev_tab, "p") /*
  469. ::doc:goto_prev_tab::
  470. goto-prev-tab [NUMBER] [TAB-SIZE]
  471.  
  472. Move NUMBER (def: 1) tab stops to the left.
  473. ::end:: */
  474. {
  475.     int tabs = 1;
  476.     VW *vw = curr_vw;
  477.     int tabsize = NUMBERP(size) ? VNUM(size) : vw->vw_Tx->tx_TabSize;
  478.     long x = glyph_col(vw->vw_Tx, vw->vw_CursorPos.pos_Col,
  479.                vw->vw_CursorPos.pos_Line);
  480.     if(NUMBERP(num))
  481.     tabs = VNUM(num);
  482.     if(tabs > 0)
  483.     {
  484.     while(tabs--)
  485.         x = (((x - 1) / tabsize)) * tabsize;
  486.     }
  487.     else if(tabs < 0)
  488.     {
  489.     while(tabs++)
  490.         x = ((x / tabsize) + 1) * tabsize;
  491.     }
  492.     if(x >= 0)
  493.     {
  494.     vw->vw_CursorPos.pos_Col = char_col(vw->vw_Tx, x,
  495.                         vw->vw_CursorPos.pos_Line);
  496.     return(sym_t);
  497.     }
  498.     vw->vw_CursorPos.pos_Col = 0;
  499.     return(sym_nil);
  500. }
  501.  
  502. _PR VALUE cmd_next_tab(VALUE num, VALUE pos, VALUE size);
  503. DEFUN("next-tab", cmd_next_tab, subr_next_tab, (VALUE num, VALUE pos, VALUE size), V_Subr3, DOC_next_tab) /*
  504. ::doc:next_tab::
  505. next-tab [NUMBER] [POS] [TAB-SIZE]
  506.  
  507. Return the position of the NUMBERth (def: 1) tab stop to the right of POS (or
  508. the cursor).
  509. Note that this doesn't return the actual *character* position of the tab
  510. stop, but the number of glyphs which have to be displayed. POS is altered.
  511. ::end:: */
  512. {
  513.     int tabs = 1;
  514.     VW *vw = curr_vw;
  515.     int tabsize = NUMBERP(size) ? VNUM(size) : vw->vw_Tx->tx_TabSize;
  516.     if(!POSP(pos))
  517.     {
  518.     pos = make_lpos(&curr_vw->vw_CursorPos);
  519.     calc_cursor_offset(vw);
  520.     VPOS(pos).pos_Col = vw->vw_LastCursorOffset;
  521.     }
  522.     if(NUMBERP(num))
  523.     tabs = VNUM(num);
  524.     if(tabs > 0)
  525.     {
  526.     while(tabs--)
  527.         VPOS(pos).pos_Col = ((VPOS(pos).pos_Col / tabsize) + 1) * tabsize;
  528.     }
  529.     else if(tabs < 0)
  530.     {
  531.     while(tabs++)
  532.         VPOS(pos).pos_Col = (((VPOS(pos).pos_Col - 1) / tabsize))
  533.                          * tabsize;
  534.     }
  535.     if(VPOS(pos).pos_Col >= 0)
  536.     return(pos);
  537.     VPOS(pos).pos_Col = 0;
  538.     return(sym_nil);
  539. }
  540.  
  541. _PR VALUE cmd_goto_next_tab(VALUE num, VALUE size);
  542. DEFUN_INT("goto-next-tab", cmd_goto_next_tab, subr_goto_next_tab, (VALUE num, VALUE size), V_Subr2, DOC_goto_next_tab, "p") /*
  543. ::doc:goto_next_tab::
  544. goto-next-tab [NUMBER] [TAB-SIZE]
  545.  
  546. Move NUMBER (def: 1) tab stops to the right
  547. ::end:: */
  548. {
  549.     int tabs = 1;
  550.     VW *vw = curr_vw;
  551.     int tabsize = NUMBERP(size) ? VNUM(size) : vw->vw_Tx->tx_TabSize;
  552.     long x = glyph_col(vw->vw_Tx, vw->vw_CursorPos.pos_Col,
  553.                vw->vw_CursorPos.pos_Line);
  554.     if(NUMBERP(num))
  555.     tabs = VNUM(num);
  556.     if(tabs > 0)
  557.     {
  558.     while(tabs--)
  559.         x = ((x / tabsize) + 1) * tabsize;
  560.     }
  561.     else if(tabs < 0)
  562.     {
  563.     while(tabs++)
  564.         x = (((x - 1) / tabsize)) * tabsize;
  565.     }
  566.     if(x >= 0)
  567.     {
  568.     vw->vw_CursorPos.pos_Col = char_col(vw->vw_Tx, x,
  569.                         vw->vw_CursorPos.pos_Line);
  570.     return(sym_t);
  571.     }
  572.     vw->vw_CursorPos.pos_Col = 0;
  573.     return(sym_nil);
  574. }
  575.  
  576. static bool
  577. prev_char(long count, POS *pos, TX *tx)
  578. {
  579.     LINE *line = tx->tx_Lines + pos->pos_Line;
  580.     if(count < 0)
  581.     return(next_char(-count, pos, tx));
  582.     while(count > 0)
  583.     {
  584.     if(count <= pos->pos_Col)
  585.     {
  586.         pos->pos_Col -= count;
  587.         count = 0;
  588.     }
  589.     else
  590.     {
  591.         count -= pos->pos_Col + 1; /* `+ 1' for the assumed '\n' */
  592.         line--;
  593.         pos->pos_Line--;
  594.         if(pos->pos_Line < 0)
  595.         return(FALSE);
  596.         pos->pos_Col = line->ln_Strlen - 1;
  597.     }
  598.     }
  599.     return(TRUE);
  600. }
  601.  
  602. static bool
  603. next_char(long count, POS *pos, TX *tx)
  604. {
  605.     LINE *line = tx->tx_Lines + pos->pos_Line;
  606.     if(count < 0)
  607.     return(prev_char(-count, pos, tx));
  608.     while(count > 0)
  609.     {
  610.     if(count < (line->ln_Strlen - pos->pos_Col))
  611.     {
  612.         pos->pos_Col += count;
  613.         count = 0;
  614.     }
  615.     else
  616.     {
  617.         count -= line->ln_Strlen - pos->pos_Col;
  618.         pos->pos_Col = 0;
  619.         pos->pos_Line++;
  620.         line++;
  621.         if(pos->pos_Line >= tx->tx_NumLines)
  622.         return(FALSE);
  623.     }
  624.     }
  625.     return(TRUE);
  626. }
  627.  
  628. _PR VALUE cmd_next_char(VALUE count, VALUE pos, VALUE tx);
  629. DEFUN("next-char", cmd_next_char, subr_next_char, (VALUE count, VALUE pos, VALUE tx), V_Subr3, DOC_next_char) /*
  630. ::doc:next_char::
  631. next-char [COUNT] [POS] [BUFFER]
  632.  
  633. Returns the position of the character COUNT (default: 1) characters after POS
  634. (or the cursor). POS is altered.
  635. ::end:: */
  636. {
  637.     if(!BUFFERP(tx))
  638.     tx = VAL(curr_vw->vw_Tx);
  639.     if(!POSP(pos))
  640.     pos = make_lpos(get_tx_cursor(VTX(tx)));
  641.     else
  642.     check_pos(VTX(tx), &VPOS(pos));
  643.     if(next_char(NUMBERP(count) ? VNUM(count) : 1, &VPOS(pos), VTX(tx)))
  644.     return(pos);
  645.     return(sym_nil);
  646. }
  647.  
  648. _PR VALUE cmd_goto_next_char(VALUE count);
  649. DEFUN_INT("goto-next-char", cmd_goto_next_char, subr_goto_next_char, (VALUE count), V_Subr1, DOC_goto_next_char, "p") /*
  650. ::doc:goto_next_char::
  651. goto-next-char [COUNT]
  652.  
  653. Moves to the character COUNT characters after the cursor.
  654. ::end:: */
  655. {
  656.     VW *vw = curr_vw;
  657.     POS tmp = vw->vw_CursorPos;
  658.     if(next_char(NUMBERP(count) ? VNUM(count) : 1, &tmp, vw->vw_Tx))
  659.     {
  660.     vw->vw_CursorPos = tmp;
  661.     return(sym_t);
  662.     }
  663.     return(sym_nil);
  664. }
  665.  
  666. _PR VALUE cmd_prev_char(VALUE count, VALUE pos, VALUE tx);
  667. DEFUN("prev-char", cmd_prev_char, subr_prev_char, (VALUE count, VALUE pos, VALUE tx), V_Subr3, DOC_prev_char) /*
  668. ::doc:prev_char::
  669. prev-char [COUNT] [POS] [BUFFER]
  670.  
  671. Returns the position of the character COUNT characters before POS (or the
  672. cursor). POS is altered.
  673. ::end:: */
  674. {
  675.     if(!BUFFERP(tx))
  676.     tx = VAL(curr_vw->vw_Tx);
  677.     if(!POSP(pos))
  678.     pos = make_lpos(get_tx_cursor(VTX(tx)));
  679.     else
  680.     check_pos(VTX(tx), &VPOS(pos));
  681.     if(prev_char(NUMBERP(count) ? VNUM(count) : 1, &VPOS(pos), VTX(tx)))
  682.     return(pos);
  683.     return(sym_nil);
  684. }
  685.  
  686. _PR VALUE cmd_goto_prev_char(VALUE count);
  687. DEFUN_INT("goto-prev-char", cmd_goto_prev_char, subr_goto_prev_char, (VALUE count), V_Subr1, DOC_goto_prev_char, "p") /*
  688. ::doc:goto_prev_char::
  689. goto-prev-char [COUNT]
  690.  
  691. Moves to the character COUNT characters before the cursor.
  692. ::end:: */
  693. {
  694.     VW *vw = curr_vw;
  695.     POS tmp = vw->vw_CursorPos;
  696.     if(prev_char(NUMBERP(count) ? VNUM(count) : 1, &tmp, vw->vw_Tx))
  697.     {
  698.     vw->vw_CursorPos = tmp;
  699.     return(sym_t);
  700.     }
  701.     return(sym_nil);
  702. }
  703.  
  704. _PR VALUE cmd_match_brackets(VALUE pos, VALUE tx, VALUE esc);
  705. DEFUN("match-brackets", cmd_match_brackets, subr_match_brackets, (VALUE pos, VALUE tx, VALUE esc), V_Subr3, DOC_match_brackets) /*
  706. ::doc:match_brackets::
  707. match-brackets [POS] [BUFFER] [ESCAPE-CHAR]
  708.  
  709. Find a bracket matching the one at POS (or the cursor). The things that match
  710. each other are,  { }, ( ), [ ], ` ', < >. POS is altered.
  711. Brackets preceded by ESCAPE-CHAR (`\' by default) are not counted.
  712. ::end:: */
  713. {
  714.     u_char esc_char;
  715.     if(!BUFFERP(tx))
  716.     tx = VAL(curr_vw->vw_Tx);
  717.     if(!POSP(pos))
  718.     pos = make_lpos(get_tx_cursor(VTX(tx)));
  719.     else
  720.     check_pos(VTX(tx), &VPOS(pos));
  721.     if(NUMBERP(esc))
  722.     esc_char = VNUM(esc);
  723.     else
  724.     esc_char = '\\';
  725.     if(find_matching_bracket(&VPOS(pos), VTX(tx), esc_char))
  726.     return(pos);
  727.     return(sym_nil);
  728. }
  729.  
  730. _PR VALUE cmd_mouse_pos(void);
  731. DEFUN("mouse-pos", cmd_mouse_pos, subr_mouse_pos, (void), V_Subr0, DOC_mouse_pos) /*
  732. ::doc:mouse_pos::
  733. mouse-pos
  734.  
  735. Return the position of the mouse pointer, relative to the display origin of
  736. the buffer in the current window.
  737. ::end:: */
  738. {
  739.     VALUE pos;
  740.     if((pos = make_lpos2(0, 0)) && sys_get_mouse_pos(&VPOS(pos), curr_vw))
  741.     return(pos);
  742.     return(sym_nil);
  743. }
  744.  
  745. static long
  746. move_down_screens(long pages)
  747. {
  748.     VW *vw = curr_vw;
  749.     long newline, rc;
  750.     newline = vw->vw_CursorPos.pos_Line + (pages * vw->vw_MaxY);
  751.     if(newline >= vw->vw_Tx->tx_NumLines)
  752.     {
  753.     newline = vw->vw_Tx->tx_NumLines - 1;
  754.     rc = FALSE;
  755.     }
  756.     else
  757.     {
  758.     vw->vw_StartLine += (pages * vw->vw_MaxY);
  759.     rc = TRUE;
  760.     }
  761.     vw->vw_CursorPos.pos_Line = newline;
  762.     adjust_cursor_to_glyph(vw);
  763.     return(rc);
  764. }
  765.  
  766. static long
  767. move_up_screens(long pages)
  768. {
  769.     VW *vw = curr_vw;
  770.     long newline, rc;
  771.     newline = vw->vw_CursorPos.pos_Line - (pages * vw->vw_MaxY);
  772.     if(newline < 0)
  773.     {
  774.     newline = 0;
  775.     rc = FALSE;
  776.     }
  777.     else
  778.     {
  779.     vw->vw_StartLine -= (pages * vw->vw_MaxY);
  780.     if(vw->vw_StartLine < 0)
  781.         vw->vw_StartLine = 0;
  782.     rc = TRUE;
  783.     }
  784.     vw->vw_CursorPos.pos_Line = newline;
  785.     adjust_cursor_to_glyph(vw);
  786.     return(rc);
  787. }
  788.  
  789. static int
  790. find_matching_bracket(POS *pos, TX *tx, u_char esc)
  791. {
  792. #define NUM_BRAC_TYPES 10
  793.     static u_char bracs[] =
  794.     {
  795.     '{', '}',
  796.     '(', ')',
  797.     '[', ']',
  798.     '`', '\'',
  799.     '<', '>'
  800.     };
  801.  
  802. /* Test for an escape character preceding COL in the string LINE. Beware
  803.    that COL is referenced more than once, so no side effects please!   */
  804. #define TST_ESC(line, col) ((col) > 0 && (line)[(col)-1] == esc)
  805.  
  806.     LINE *line = tx->tx_Lines + pos->pos_Line;
  807.     if(pos->pos_Col < line->ln_Strlen)
  808.     {
  809.     u_char startc = line->ln_Line[pos->pos_Col];
  810.     long i;
  811.     for(i = 0; i < NUM_BRAC_TYPES; i++)
  812.     {
  813.         if(startc == bracs[i])
  814.         break;
  815.     }
  816.     if(!TST_ESC(line->ln_Line, pos->pos_Col) && (i < NUM_BRAC_TYPES))
  817.     {
  818.         long x = pos->pos_Col;
  819.         long y = pos->pos_Line;
  820.         long braccount = 1;
  821.         bool found = FALSE;
  822.         if(i & 1)
  823.         {
  824.         /* search backwards */
  825.         u_char endc = bracs[i - 1];
  826.         while(!found)
  827.         {
  828.             u_char c;
  829.             if(--x < 0)
  830.             {
  831.             if(--y < 0)
  832.             {
  833.                 cmd_signal(sym_error,
  834.                        LIST_1(MKSTR("No matching bracket")));
  835.                 return(FALSE);
  836.             }
  837.             line--;
  838.             x = line->ln_Strlen - 1;
  839.             }
  840.             c = line->ln_Line[x];
  841.             if(c == startc)
  842.             {
  843.             if(!TST_ESC(line->ln_Line, x))
  844.                 braccount++;
  845.             }
  846.             else if(c == endc)
  847.             {
  848.             if(!TST_ESC(line->ln_Line, x) && !(--braccount))
  849.                 found = TRUE;
  850.             }
  851.         }
  852.         }
  853.         else
  854.         {
  855.         /* search forwards */
  856.         u_char endc = bracs[i + 1];
  857.         while(!found)
  858.         {
  859.             u_char c;
  860.             if(++x >= line->ln_Strlen)
  861.             {
  862.             if(++y >= tx->tx_NumLines)
  863.             {
  864.                 cmd_signal(sym_error,
  865.                        LIST_1(MKSTR("No matching bracket")));
  866.                 return(FALSE);
  867.             }
  868.             line++;
  869.             x = 0;
  870.             }
  871.             c = line->ln_Line[x];
  872.             if(c == startc)
  873.             {
  874.             if(!TST_ESC(line->ln_Line, x))
  875.                 braccount++;
  876.             }
  877.             else if(c == endc)
  878.             {
  879.             if(!TST_ESC(line->ln_Line, x) && !(--braccount))
  880.                 found = TRUE;
  881.             }
  882.         }
  883.         }
  884.         pos->pos_Col = x;
  885.         pos->pos_Line = y;
  886.         return(TRUE);
  887.     }
  888.     }
  889.     cmd_signal(sym_error, LIST_1(MKSTR("No opening bracket")));
  890.     return(FALSE);
  891. }
  892.  
  893. void
  894. movement_init(void)
  895. {
  896.     ADD_SUBR(subr_screen_top_line);
  897.     ADD_SUBR(subr_screen_bottom_line);
  898.     ADD_SUBR(subr_screen_first_column);
  899.     ADD_SUBR(subr_screen_last_column);
  900.     ADD_SUBR(subr_goto_char);
  901.     ADD_SUBR(subr_goto_glyph);
  902.     ADD_SUBR(subr_centre_display);
  903.     ADD_SUBR(subr_next_screen);
  904.     ADD_SUBR(subr_prev_screen);
  905.     ADD_SUBR(subr_buffer_end);
  906.     ADD_SUBR(subr_goto_buffer_end);
  907.     ADD_SUBR(subr_buffer_start);
  908.     ADD_SUBR(subr_goto_buffer_start);
  909.     ADD_SUBR(subr_line_end);
  910.     ADD_SUBR(subr_goto_line_end);
  911.     ADD_SUBR(subr_line_start);
  912.     ADD_SUBR(subr_goto_line_start);
  913.     ADD_SUBR(subr_next_line);
  914.     ADD_SUBR(subr_goto_next_line);
  915.     ADD_SUBR(subr_prev_line);
  916.     ADD_SUBR(subr_goto_prev_line);
  917.     ADD_SUBR(subr_left_char);
  918.     ADD_SUBR(subr_goto_left_char);
  919.     ADD_SUBR(subr_right_char);
  920.     ADD_SUBR(subr_goto_right_char);
  921.     ADD_SUBR(subr_prev_tab);
  922.     ADD_SUBR(subr_goto_prev_tab);
  923.     ADD_SUBR(subr_next_tab);
  924.     ADD_SUBR(subr_goto_next_tab);
  925.     ADD_SUBR(subr_next_char);
  926.     ADD_SUBR(subr_goto_next_char);
  927.     ADD_SUBR(subr_prev_char);
  928.     ADD_SUBR(subr_goto_prev_char);
  929.     ADD_SUBR(subr_match_brackets);
  930.     ADD_SUBR(subr_mouse_pos);
  931. }
  932.